{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5632f4b9",
   "metadata": {},
   "source": [
    "# Working with Branch Versioning\n",
    "\n",
    "Branch versioning is the mechanism behind long transaction editing for web feature layers (feature services). Enabling the Version Management capability facilitates the creation of named versions and version management. Editors can then work with their own named version in the web feature layer, update the data, and reconcile and post their changes with the default version when finished.\n",
    "\n",
    "This notebook will demonstrate some common methods for administering and edting with branch versioned feature services."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "606e99c8-a110-4826-b5f3-02dca3cf713c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from arcgis.gis import GIS\n",
    "from arcgis.features import FeatureLayerCollection"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "82cf48de-d5a5-496e-bedd-2626f10ba350",
   "metadata": {},
   "outputs": [],
   "source": [
    "gis = GIS(profile=\"your_enterprise_profile\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6fcdc790-4f2e-4d66-8b9f-9ca65f4ee2a2",
   "metadata": {},
   "source": [
    "#### Access the VersionManager object from a Feature Layer Collection\n",
    "\n",
    "A feature service containing a collection of one or more feature layers is a `Feature Layer Collection`. If the feature service is published with the **Version Management** option, the feature layer collection will have a property called `versions` containing the `VersionManager` object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b197173e-61b3-49bf-a86a-3d16280f2249",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div class=\"item_container\" style=\"height: auto; overflow: hidden; border: 1px solid #cfcfcf; border-radius: 2px; background: #f6fafa; line-height: 1.21429em; padding: 10px;\">\n",
       "                    <div class=\"item_left\" style=\"width: 210px; float: left;\">\n",
       "                       <a href='https://example.server.com/portal/home/item.html?id=3d70c5cc25874f7790dbafd37c8c042f' target='_blank'>\n",
       "                        <img src='' width='200' height='133' class=\"itemThumbnail\">\n",
       "                       </a>\n",
       "                    </div>\n",
       "\n",
       "                    <div class=\"item_right\"     style=\"float: none; width: auto; overflow: hidden;\">\n",
       "                        <a href='https://example.server.com/portal/home/item.html?id=3d70c5cc25874f7790dbafd37c8c042f' target='_blank'><b>BranchVersioned_FLC</b>\n",
       "                        </a>\n",
       "                        <br/>Non-controller, sync enabled, branch versioned collection of feature classes<br/><img src='https://example.server.com/portal/home/js/jsapi/esri/css/images/item_type_icons/featureshosted16.png' style=\"vertical-align:middle;\" width=16 height=16>Feature Layer Collection by api_data_owner\n",
       "                        <br/>Last Modified: July 04, 2025\n",
       "                        <br/>0 comments, 2 views\n",
       "                    </div>\n",
       "                </div>\n",
       "                "
      ],
      "text/plain": [
       "<Item title:\"BranchVersioned_FLC\" type:Feature Layer Collection owner:api_data_owner>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Version Manager from FeatureLayerCollection\n",
    "item = gis.content.search(\"BranchVersioned_FLC\")\n",
    "feature_layer_item = [i for i in item if i.type == \"Feature Service\"][0]\n",
    "feature_layer_item"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "c8258e4b-6280-4119-a1fa-a568bc544211",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<FeatureLayer url:\"https://example.server.com/server/rest/services/BranchVersioned_FLC/FeatureServer/0\">,\n",
       " <FeatureLayer url:\"https://example.server.com/server/rest/services/BranchVersioned_FLC/FeatureServer/1\">,\n",
       " <FeatureLayer url:\"https://example.server.com/server/rest/services/BranchVersioned_FLC/FeatureServer/2\">]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "feature_layer_collection = FeatureLayerCollection(feature_layer_item.url, gis)\n",
    "feature_layer_collection.layers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "cb8cd8b9-7716-431e-a9b8-c613486b4e34",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "< VersionManager @ https://example.server.com/server/rest/services/BranchVersioned_FLC/VersionManagementServer >"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "flc_version_manager = feature_layer_collection.versions\n",
    "flc_version_manager"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12292f3f-633b-4ec4-a41a-ca9373b9f217",
   "metadata": {},
   "source": [
    "### List branch versions\n",
    "\n",
    "`VersionManager.all` will return all branch versions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "5a6b5d8a-d509-4231-951e-76efbad018c6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<Version sde.DEFAULT @ {BD3F4817-9A00-41AC-B0CC-58F78DBAE0A1}>,\n",
       " <Version API_DATA_OWNER.editor1 @ {E600C422-341B-4B34-BF77-1986EBDCECDA}>]"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "version_manager.all"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56bfb69f-7504-4f5f-be0d-abbc2125f26e",
   "metadata": {},
   "source": [
    "## Find branch versions\n",
    "\n",
    "### VersionManager.get()\n",
    "Use `VersionManager.get(<version name string>, <mode>)` to access the `Version` object. \n",
    "- Set the mode to start a `read` session or an `edit` session.\n",
    "- Using the context manager will help manage starting and stopping the edit session."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "353b4c8d-5256-4538-99f5-729c77c1f6ea",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "API_DATA_OWNER.editor1\n"
     ]
    }
   ],
   "source": [
    "with version_manager.get(\"API_DATA_OWNER.editor1\", None) as version:\n",
    "    print(version.properties.versionName)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5f374740-6008-48ba-a54d-01cf8c93c6b9",
   "metadata": {},
   "source": [
    "### VersionManager.get_by_name()\n",
    "Use `VersionManager.get_by_name(<version owner string>, <version name string>, <mode>) to access a `Version` object.\n",
    "\n",
    "The `get_by_name` method uses the 'versionInfos` endpoint and can be much faster at retrieving a specific version when there is a large quantity of branch versions to consider."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "28bae628-31c1-4ca6-9f98-ef464055526e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "API_DATA_OWNER.editor1\n"
     ]
    }
   ],
   "source": [
    "with version_manager.get_by_name(\"API_DATA_OWNER\", \"editor1\", None) as version:\n",
    "    print(version.properties.versionName)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "046455f5-1fa2-436b-a277-760ce2354450",
   "metadata": {},
   "source": [
    "## Creating Branch Versions\n",
    "\n",
    "Create one or more branch versions with `VersionManager.create()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "f59546a5-35d9-42d1-9552-ac1fb3a5352b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Created version: ARCGIS_PYTHON.workplan_a123\n",
      "Created version: ARCGIS_PYTHON.workplan_b456\n",
      "Created version: ARCGIS_PYTHON.workplan_c789\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[<Version sde.DEFAULT @ {BD3F4817-9A00-41AC-B0CC-58F78DBAE0A1}>,\n",
       " <Version API_DATA_OWNER.editor1 @ {E600C422-341B-4B34-BF77-1986EBDCECDA}>,\n",
       " <Version ARCGIS_PYTHON.workplan_a123 @ {7C185974-A8C7-492E-8D5F-F6C43DAE1113}>,\n",
       " <Version ARCGIS_PYTHON.workplan_b456 @ {9340D706-4861-4B43-A012-20AF82DF1AB4}>,\n",
       " <Version ARCGIS_PYTHON.workplan_c789 @ {212EA99C-B7D1-4C39-A42D-9689BCA43849}>]"
      ]
     },
     "execution_count": 82,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "versions = [\n",
    "    {\"name\": \"workplan_a123\", \"permission\": \"public\", \"description\": \"Version for workplan A123\"},\n",
    "    {\"name\": \"workplan_b456\", \"permission\": \"public\", \"description\": \"Version for workplan A123\"},\n",
    "    {\"name\": \"workplan_c789\", \"permission\": \"public\", \"description\": \"Version for workplan A123\"},\n",
    "]\n",
    "\n",
    "for version in versions:\n",
    "    new_version = version_manager.create(**version)\n",
    "    print(\"Created version:\", new_version[\"versionInfo\"][\"versionName\"])\n",
    "\n",
    "version_manager.all"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e5b638d8-6a8b-4367-bee1-2ded83adc0c3",
   "metadata": {},
   "source": [
    "## Branch Versioning Edit Sessions\n",
    "A branch versioning edit session is the act of obtaining shared and exclusive locks on the feature class to prevent corruption in the branch version. Calling `version.startReading` will set a shared lock on the version which prevents another session from obtaining an exclusive lock. Other sessions can still access the version as read-only. Calling `version.startEditing` will set the exclusive lock which will prevent read access and write access to the version.\n",
    "\n",
    "Keeping track of where one is within the edit sessions is made simple with a built in context manager. Use the `mode` parameter in the `VersionManager.get` call to set the initial `read` session. Alter the mode within the context manager to set the `edit` session."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "bf6638d3-be7d-44d3-84f1-3f3586bdcea7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Read lock initiated from function:\n",
      "\tHas read lock: True\n",
      "\tHas edit lock: False\n",
      "Edit lock initiated from mode setter:\n",
      "\tHas read lock: False\n",
      "\tHas edit lock: True\n",
      "Lock status after exiting context manager:\n",
      "\tHas read lock: False\n",
      "\tHas edit lock: False\n"
     ]
    }
   ],
   "source": [
    "with version_manager.get(\"ARCGIS_PYTHON.workplan_c789\", mode=\"read\") as version:\n",
    "    print(\"Read lock initiated from function:\")\n",
    "    print(\"\\tHas read lock:\", version.properties[\"isBeingRead\"])\n",
    "    print(\"\\tHas edit lock:\", version.properties[\"isBeingEdited\"])\n",
    "\n",
    "    version.mode = \"edit\"\n",
    "\n",
    "    print(\"Edit lock initiated from mode setter:\")\n",
    "    print(\"\\tHas read lock:\", version.properties[\"isBeingRead\"])\n",
    "    print(\"\\tHas edit lock:\", version.properties[\"isBeingEdited\"])\n",
    "\n",
    "print(\"Lock status after exiting context manager:\")\n",
    "print(\"\\tHas read lock:\", version.properties[\"isBeingRead\"])\n",
    "print(\"\\tHas edit lock:\", version.properties[\"isBeingEdited\"])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e6ac5ee-1d9a-481a-a81d-200d4defa43d",
   "metadata": {},
   "source": [
    "Alternately, call `start_reading` and `start_editing` from a `Version`. Remember to call `stop_editing` and `stop_reading` when finished.\n",
    "\n",
    "```python\n",
    "version = version_manager.get(\"ARCGIS_PYTHON.workplan_c789\", mode=None)\n",
    "version.start_reading()\n",
    "version.start_editing()\n",
    "\n",
    "# Do some work\n",
    "\n",
    "version.stop_editing()\n",
    "version.stop_reading()\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fcdda81e-5673-4192-9bef-9292630b17e4",
   "metadata": {},
   "source": [
    "### Editing Branch Versioned Layers\n",
    "\n",
    "The `Version` object has a method, `Version.edit` that allows one to pass in adds, updates and/or deletes.\n",
    "\n",
    "https://developers.arcgis.com/python/latest/api-reference/arcgis.features.managers.html#arcgis.features._version.Version.edit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "bacb30a6-f192-40f0-a087-094b62b31091",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initial rows: 9\n",
      "New row added...\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'OBJECTID': 414,\n",
       " 'Jurisdiction': 'Rigby',\n",
       " 'Height': 10.5,\n",
       " 'GlobalID': '{F09308DB-8B9F-4B0C-82FD-FA597186671F}',\n",
       " 'created_user': 'arcgis_python',\n",
       " 'created_date': 1751664384000,\n",
       " 'last_edited_user': 'arcgis_python',\n",
       " 'last_edited_date': 1751664384000}"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from arcgis.features import FeatureSet\n",
    "adds = {\"features\":[\n",
    "         {\n",
    "            \"attributes\":{\n",
    "               \"Jurisdiction\":\"Rigby\",\n",
    "               \"Height\": 10.5,\n",
    "            },\n",
    "            \"geometry\":{\n",
    "               \"x\":-8911900.3001000006,\n",
    "               \"y\":5170534.9993999973,\n",
    "               \"spatialReference\":{\n",
    "                  \"wkid\":102100,\n",
    "               }\n",
    "            }\n",
    "         }\n",
    "      ]}\n",
    "feature = FeatureSet.from_dict(adds)\n",
    "\n",
    "target_version_name = \"ARCGIS_PYTHON.workplan_b456\"\n",
    "feature_layer = [l for l in feature_layer_collection.layers if l.properties.name == \"ObservationTowers\"][0]\n",
    "initial_rows = feature_layer.query(\"1=1\", gdb_version=target_version_name)\n",
    "print(\"Initial rows:\", len(initial_rows))\n",
    "\n",
    "with version_manager.get(target_version_name, mode=\"read\") as version:\n",
    "    version.mode = \"edit\"\n",
    "    \n",
    "    version.edit(feature_layer, adds=feature)\n",
    "    version.save_edits = True\n",
    "    print(\"New row added...\")\n",
    "    \n",
    "new_feature = feature_layer.query(\"Jurisdiction = 'Rigby' AND height = 10.5\", gdb_version=version.properties[\"versionName\"])\n",
    "new_feature.features[0].attributes\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09bde5d2-0fcc-4219-8409-189d823b247b",
   "metadata": {},
   "source": [
    "## Find Version Differences\n",
    "\n",
    "The differences method allows you to view differences between the current version and the default version. The two versions can be compared to check for the multiple conditions.\n",
    "\n",
    "https://developers.arcgis.com/python/latest/api-reference/arcgis.features.managers.html#arcgis.features._version.Version.differences"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "a1680751-bdd7-45d2-bde0-c28ca25003cd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'features': [{'layerId': 0,\n",
       "   'inserts': [{'attributes': {'OBJECTID': 414,\n",
       "      'Jurisdiction': 'Rigby',\n",
       "      'Height': 10.5,\n",
       "      'GlobalID': '{F09308DB-8B9F-4B0C-82FD-FA597186671F}',\n",
       "      'created_user': 'arcgis_python',\n",
       "      'created_date': 1751664384000,\n",
       "      'last_edited_user': 'arcgis_python',\n",
       "      'last_edited_date': 1751664384000},\n",
       "     'geometry': {'x': -8911900.3001, 'y': 5170534.999399997}}]}],\n",
       " 'success': True}"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "target_version_name = \"ARCGIS_PYTHON.workplan_b456\"\n",
    "observation_towers_layerid = feature_layer.properties.id\n",
    "observation_towers_layerid\n",
    "with version_manager.get(target_version_name, \"read\") as version:\n",
    "    diff = version.differences(\n",
    "        result_type=\"features\", layers=[observation_towers_layerid], future=False\n",
    "    )\n",
    "diff"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "394df0dd-6a95-4c21-bcb2-781ba840b5d0",
   "metadata": {},
   "source": [
    "## Reconcile and Post Edits\n",
    "Use the `Version` object to reconcile and post edits to the Default version. Use the `rows` argument to pass in a subset of features to post to Default. Passing in a subset of features is known as a \"partial post\".\n",
    "\n",
    "https://developers.arcgis.com/python/latest/api-reference/arcgis.features.managers.html#arcgis.features._version.Version.post"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "id": "734fdb69-53e1-42c6-8bab-c0b240f87424",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n"
     ]
    }
   ],
   "source": [
    "target_version_name = \"ARCGIS_PYTHON.workplan_a123\"\n",
    "with version_manager.get(target_version_name, \"read\") as version:\n",
    "    version.mode = \"edit\"\n",
    "    result = version.reconcile(\n",
    "        end_with_conflict=True,\n",
    "        conflict_detection=\"byAttribute\",\n",
    "        with_post=False,\n",
    "        future=False,\n",
    "    )\n",
    "\n",
    "    if result.get(\"success\"):\n",
    "        did_post = version.post(rows=None, future=False)\n",
    "        print(did_post)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "1802feee-193f-4f7b-b069-87274b8a241f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rows_in_default = feature_layer.query(\"1=1\")\n",
    "len(rows_in_default)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4d08baf-cbb2-4b28-b211-8ecb01f1a304",
   "metadata": {},
   "source": [
    "## Delete Versions\n",
    "After successfully reconciling and posting edits to Default, the version(s) can be deleted."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "id": "1be8430e-41d5-49f1-9171-26fce22173b8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Deleted: ARCGIS_PYTHON.workplan_a123\n",
      "Deleted: ARCGIS_PYTHON.workplan_b456\n",
      "Deleted: ARCGIS_PYTHON.workplan_c789\n"
     ]
    }
   ],
   "source": [
    "version_manager = VersionManager(version_management_server_url, gis)\n",
    "for version in version_manager.all:\n",
    "    if \"workplan\" in version.properties[\"versionName\"]:\n",
    "        version.delete()\n",
    "        print(\"Deleted:\", version.properties[\"versionName\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ecfd55d-06fc-4270-b418-abbf31712fbc",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "daily",
   "language": "python",
   "name": "daily"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
